///////////////////////////////////////////////////////////
//  ResolveDBRequest.cpp
//  Implementation of the Class ResolveDBRequest
//  Created on:      23-sep-2007 21:26:24
///////////////////////////////////////////////////////////

#include "ResolveDBRequest.h"
#include "PrintReg.h"

#define MAX_SIZE_SCHEMA 20
#define MAX_SIZE_REG 4096

#ifndef UNO
	#define UNO 1
#endif

#ifndef ZERO
	#define ZERO 0
#endif

Cproperties ResolveDBRequest::proper("pknumbers.properties");

ResolveDBRequest::ResolveDBRequest(){

}



ResolveDBRequest::~ResolveDBRequest(){

}

ListPersistent ResolveDBRequest::resolveAnd(OQLAndCondition& condition){
	
	int i = ZERO;
	ListPersistent list;
	OQLCondition* simpleCondition = NULL;
	IntersectionOperation intersection;
	
	std::multimap<int,ListPersistent*> mapLs;
	bool salir = false;
	
	// resuelve el low y lo pone en el mapa de listas
	OQLCondition* lowCond = condition.extractLowLevelCond();
	ListPersistent listCondition;
	listCondition = lowCond->resolve(lowCond->getTable());
	if(listCondition.getSize() == 0)
	{
		return list;
	}
	campo* key = listCondition.getPersistent(0)->getPrimaryKey();
	listCondition.sort(key[0].nombre);

	mapLs.insert(make_pair(listCondition.getSize(), &listCondition));	
	
	for(i = ZERO; i < condition.getSize() && !salir; i++)
	{
		simpleCondition = condition.getCondition(i);
		if (simpleCondition == lowCond){
			continue;
		}
		if (simpleCondition->getName()=="OQLSimpleCondition"){
			ListUtil::applyFilter(listCondition, *(OQLSimpleCondition*)simpleCondition);
		}else{
			if (simpleCondition->getName()=="OQLRangeCondition"){
				ListUtil::applyRangeFilter(listCondition, *(OQLRangeCondition*)simpleCondition);
			}else{
				ListPersistent listAux = simpleCondition->resolve(simpleCondition->getTable());
				if (listAux.getSize()!=0){
					key = listAux.getPersistent(0)->getPrimaryKey();
					listAux.sort(key[0].nombre);
				}else{
					salir = true;
					i = 0;
				}
				mapLs.insert(make_pair(listAux.getSize(), &listAux));	
			}
		}
	}
	
	std::multimap<int, ListPersistent*>::iterator it;
	it = mapLs.upper_bound(0);
	ListPersistent *listReturn = it->second;
	i = 1;
	while ( i < (int)mapLs.size()){
		++it;
		ListPersistent *lsTwo = it->second;
		intersection.resolveOperation(*listReturn, *lsTwo);
		lsTwo->clear();
		i++;
	}

	return *listReturn;

}


ListPersistent ResolveDBRequest::resolveOr(OQLOrCondition& condition){
	int i = ZERO;
	ListPersistent list;
	OQLCondition* simpleCondition = NULL;
	UnionOperation unionOp;
	
	for(i = ZERO; i < condition.getSize(); i++)
	{
		ListPersistent listCondition;
		simpleCondition = condition.getCondition(i);
		if(i == ZERO)
			list = simpleCondition->resolve(simpleCondition->getTable());
		else
		{
			listCondition = simpleCondition->resolve(simpleCondition->getTable());
			unionOp.resolveOperation(list, listCondition);
		}
	}
		
	return list;
}


ListPersistent ResolveDBRequest::resolveSimple(OQLSimpleCondition& condition){
	
	ListPersistent list;
	IndexOrganization *index = new IndexOrganization();
	int handler = ZERO;
	int indice = -1;
	int res = ZERO;
	unsigned int sizeReg = ZERO;
	void *reg;
	campo* esquema = NULL;
	campo* campoAtrib = condition.getAttribute();
	std::string nombreAtributo("");
	
	handler = index->I_OPEN(condition.getTable().c_str(), READ);

	if(handler > ZERO)
	{
		nombreAtributo += campoAtrib[0].nombre;
		list.setAtributte(nombreAtributo);
		indice = index->I_IS_INDEX(handler, campoAtrib);
		if( indice == RES_NO_EXISTE_INDICE ){
			indice = index->I_ADD_INDEX(handler, campoAtrib);
		}
		
		Persistent* object = FactoryPersistent::getObjectFromTable(condition.getTable());
		if (!object){
			index->I_CLOSE(handler);
			delete index;
			throw IndexException();
		}
		
		esquema = object->getSchema();
		
		delete object;

		sizeReg = RecordHandler::REG_SIZEOF(esquema);
		reg = malloc(sizeReg);
		memset(reg, ZERO, sizeReg);
		
		RecordHandler::REG_SET(reg,esquema, campoAtrib[0].nombre,condition.getValue().c_str());
		
//		PrintReg::PrintRecordStation(reg);
		
		if (index->I_START(handler, indice, (char*)condition.getOperator().c_str(), reg) != RES_OK){
			free(reg);
			index->I_CLOSE(handler);
			delete index;
			throw IndexException();
		}
		
		res = index->I_READ_NEXT(handler, indice, reg);
		
//		unsigned int contador = 0;
		
		while(res != RES_EOF && res != RES_ERROR)
		{
			object = FactoryPersistent::getObjectFromTable(condition.getTable());
			object->loadRegistro(reg);
				
			list.insertSort(object);
//			contador++;
			res = index->I_READ_NEXT(handler, indice, reg);
		}		
//		std::cout << "TOTAL DE REGISTROS DEVUELTOS: " << contador << std::endl;
		
		index->I_CLOSE(handler);
		
		free(reg);
		
	}else{
		// si viene RES_NO_EXISTE devuelvo la lista vacia
		if (handler == RES_NULL)
			throw IndexException();
	}
	
	delete index;
	return list;
}


void ResolveDBRequest::savePersistent(std::string table, Persistent* object){
	
	campo * esquema = NULL;
	campo * pk = NULL;
	IndexOrganization *index = new IndexOrganization();
	void* reg = NULL; 
	int handler = ZERO;
	
	if (proper.getSize() < 1){
		proper.CargarMapa();
	}

	std::string pkStr = proper.GetKey(table);
	if (pkStr!=""){
		int pkInt = atoi(pkStr.c_str());
		pkInt++;
		std::stringstream ss;
		ss.str("");
		ss << pkInt;
		proper.updateKeyValue(table,ss.str());
		object->setKey(ss.str());
	}
	else
	{	
		//MAXI
		// no se tira un error porque el Station no necesita
		// key autoincrementada entonces no tiene q entrar en 
		// el if. Es el unico caso que no entra.
	}
	
	esquema = object->getSchema();
	pk = object->getPrimaryKey();
	int size = RecordHandler::REG_SIZEOF(esquema);
	reg = malloc(size);
	memset(reg, ZERO, size);
	object->saveInRegistro((void*)reg);
	
//	char auxptrCode[5 + UNO];
//	char auxptrNumber[8 + UNO];
//	char auxptrElevation[10 + UNO];
//	char auxptrLatitude[10 + UNO];
//	char auxptrLongitude[10 + UNO];
//	unsigned int sizeRegistro = (*((int*)reg));
//	
//	char *current = ((char*)reg) + sizeof(unsigned int);
//	
//	memcpy(auxptrCode, current, 5);
//	current += 5;
//	memcpy(auxptrNumber, current, 8);
//	current += 8;
//	memcpy(auxptrElevation, current, 10);
//	current += 10;
//	memcpy(auxptrLatitude, current, 10);
//	current += 10;
//	memcpy(auxptrLongitude, current, 10);
//	
//	std::cout << "SIZE REGISTRO: " << sizeRegistro << std::endl;
//	std::cout << "CODE: " << auxptrCode << std::endl;
//	std::cout << "NUMBER: " << auxptrNumber << std::endl;
//	std::cout << "ELEVATION: " << auxptrElevation << std::endl;
//	std::cout << "LATITUDE: " << auxptrLatitude << std::endl;
//	std::cout << "LONGITUDE: " << auxptrLongitude << std::endl;
	

	
	handler = index->I_CREATE(table.c_str(), esquema, pk);

	handler = index->I_OPEN(table.c_str(), WRITE);
	
	if (handler >= 0){
		if (index->I_WRITE(handler, reg)!=RES_OK){
			//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
			//no devuelvo nada pq solo station se repite
			//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		}
		index->I_CLOSE(handler);
	}else{
		free(reg);
		delete index;
		throw IndexException();
	}

	free(reg);
	delete index;
	
}

ListPersistent ResolveDBRequest::resolveMin(OQLMinCondition& condition){
	
	ListPersistent list;
	IndexOrganization *index = new IndexOrganization();
	void *reg = NULL; 
	int handler = ZERO;
	int indice = ZERO;
	Persistent* object = NULL;
	campo* campoAtrib = condition.getAttribute();
	campo* esq = NULL;
	
	handler = index->I_OPEN(condition.getTable().c_str(), WRITE);
	
	if(handler != RES_NULL && handler != RES_NO_EXISTE)
	{
		indice = index->I_IS_INDEX(handler, campoAtrib);
		if(indice == RES_NO_EXISTE_INDICE)
			indice = index->I_ADD_INDEX(handler, campoAtrib);
		
		object = FactoryPersistent::getObjectFromTable(condition.getTable());
		if (!object){
			index->I_CLOSE(handler);
			delete index;
			throw IndexException();
		}
		
		esq = object->getSchema();
		
		reg = malloc(RecordHandler::REG_SIZEOF(esq));
		
		index->I_MIN(handler, indice, reg);
				
		object->loadRegistro(reg);
		
		list.insertSort(object);
		
		index->I_CLOSE(handler);
		free(reg);
	}
		
	delete index;
	return list;
}


ListPersistent ResolveDBRequest::resolverMax(OQLMaxCondition& condition){
	ListPersistent list;
	IndexOrganization *index = new IndexOrganization();
	void *reg = NULL; 
	int handler = ZERO;
	int indice = ZERO;
	Persistent* object = NULL;
	campo* campoAtrib = condition.getAttribute();
	campo* esq = NULL;
	
	handler = index->I_OPEN(condition.getTable().c_str(), WRITE);
	
	if(handler != RES_NULL && handler != RES_NO_EXISTE)
	{
		indice = index->I_IS_INDEX(handler, campoAtrib);
		if(indice == RES_NO_EXISTE_INDICE)
			indice = index->I_ADD_INDEX(handler, campoAtrib);
		
		object = FactoryPersistent::getObjectFromTable(condition.getTable());
		if (!object){
			index->I_CLOSE(handler);
			delete index;
			throw IndexException();
		}
		
		esq = object->getSchema();
		
		reg = malloc(RecordHandler::REG_SIZEOF(esq));

		index->I_MAX(handler, indice, reg);
				
		object->loadRegistro(reg);
		
		list.insertSort(object);
	
		index->I_CLOSE(handler);
		free(reg);
	}
		
	delete index;
	return list;
}


double ResolveDBRequest::resolveMedia(OQLMediaCondition& condition){
	
	Persistent* objectMax = NULL;
	Persistent* objectMin = NULL;
	
	OQLMaxCondition opMax;
	opMax.setAttribute(condition.getAttribute());
	opMax.setTable(condition.getTable());
	
	ListPersistent lsMax = ResolveDBRequest::resolverMax(opMax);
	
	if (lsMax.getSize()==0){
		return 0;
	}
	
	OQLMinCondition opMin;
	opMin.setAttribute(condition.getAttribute());
	opMin.setTable(condition.getTable());
	
	ListPersistent lsMin = ResolveDBRequest::resolveMin(opMin);
	
	if (lsMin.getSize()==0){
		if (lsMax.getSize()>0)
			lsMax.clear();
		return 0;
	}
	
	objectMax = lsMax.getFirst();
	objectMin = lsMin.getFirst();
	
	std::string value1 = objectMax->getValor(condition.getAttribute()[0].nombre);
	std::string value2 = objectMin->getValor(condition.getAttribute()[0].nombre);
	
	lsMax.clear();
	lsMin.clear();
	
	if (NumberUtil::isNumber(value1) && NumberUtil::isNumber(value2)){
    	double doubleOne = atof(value1.c_str());
    	double doubleTwo = atof(value2.c_str());
    	return ((doubleOne + doubleTwo) / 2);
	}else{
		// TODO lanzar excepcion
	}
	
	return 0;
	
}

ListPersistent ResolveDBRequest::resolverRange(OQLSimpleCondition& inf, OQLSimpleCondition& sup){
	ListPersistent list;
	IndexOrganization *index = new IndexOrganization();
	int handler = ZERO;
	int indice = -1;
	int res = ZERO;
	unsigned int sizeReg = ZERO;
	void *reg;
	bool stop = false;
	campo* esquema = NULL;
	campo* campoAtrib = inf.getAttribute();
	std::string nombreAtributo("");
	
	handler = index->I_OPEN(inf.getTable().c_str(), READ);

	if(handler > ZERO)
	{
		nombreAtributo += campoAtrib[0].nombre;
		list.setAtributte(nombreAtributo);
		indice = index->I_IS_INDEX(handler, campoAtrib);
		if( indice == RES_NO_EXISTE_INDICE ){
			indice = index->I_ADD_INDEX(handler, campoAtrib);
		}
		
		Persistent* object = FactoryPersistent::getObjectFromTable(inf.getTable());
		if (!object){
			index->I_CLOSE(handler);
			delete index;
			throw IndexException();
		}
		
		esquema = object->getSchema();
		
		delete object;

		sizeReg = RecordHandler::REG_SIZEOF(esquema);
		reg = malloc(sizeReg);
		memset(reg, ZERO, sizeReg);
		
		RecordHandler::REG_SET(reg,esquema, campoAtrib[0].nombre,inf.getValue().c_str());
		
//		PrintReg::PrintRecordStation(reg);
		
		if (index->I_START(handler, indice, (char*)inf.getOperator().c_str(), reg) != RES_OK){
			free(reg);
			index->I_CLOSE(handler);
			delete index;
			throw IndexException();
		}
		
		res = index->I_READ_NEXT(handler, indice, reg);

		while(res != RES_EOF && res != RES_ERROR && !stop)
		{
			object = FactoryPersistent::getObjectFromTable(inf.getTable());
			object->loadRegistro(reg);
			if(object->getValor(nombreAtributo) > sup.getValue()){
				stop = true;
				delete object;
			}else{
				list.insertSort(object);
				res = index->I_READ_NEXT(handler, indice, reg);
			}
		}
		
		index->I_CLOSE(handler);
		
		free(reg);
		
	}else{
		// si viene RES_NO_EXISTE devuelvo la lista vacia
		if (handler == RES_NULL)
			throw IndexException();
	}
	
	delete index;
	return list;
}


